#ifndef		__POOL_H_INCLUDED__
#define		__POOL_H_INCLUDED__

#include	<cstdlib>
#include	<cstring>
#include	<list>

/**
 * Non-threadsafe Object pool.
 */
template<class T>
class Pool {

	/**
	 * Memory chunk.
	 */
	struct Block {
		Block * next;
		bool used;
		T * data;

		inline void Init() {
			next = nullptr;
			used = false;
			data = (T *)((char *)this + sizeof(Block));
		}
	};

public:
	Pool(size_t step) : _step(step), _block_size(sizeof(Block) + sizeof(T)), _head(new Block) {
		_head->Init();
		__Extend();
	}

	~Pool() {
		Clear();
		for (auto mem : _mems) delete[] mem;
		_mems.clear();
	}

	/**
	 * Alloc new instance of T with parameters for contructor.
	 *
	 * \param args... Contructor paramters.
	 * \return New instance of type T.
	 */
	template<typename ... Args>
	T * Alloc(Args && ... args) {
		if (!_head->next && !__Extend()) return nullptr;

		Block * block = _head->next;
		_head->next = _head->next->next;
		block->used = true;

		return new (block->data) T(std::forward<Args>(args)...);
	}

	/**
	 * Recycle instance of T.
	 *
	 * \param p Object to recycle.
	 */
	void Free(T * p) {
		if (!p) return;
		p->~T();
		memset(p, 0, sizeof(T));

		Block * block = (Block *)((char *)p - sizeof(Block));
		block->used = false;
		block->next = _head->next;
		_head->next = block;
	}

	/**
	 * Force recycle all used objects.
	 */
	void Clear() {
		for (auto mem : _mems) {
			for (size_t n = 0; n < _step; ++n) {
				Block * block = (Block *)(mem + n * _block_size);
				if (block->used) {
					block->used = false;
					block->data->~T();
					memset(block->data, 0, sizeof(T));

					block->next = _head->next;
					_head->next = block;
				}
			}
		}
	}

private:
	bool __Extend() {
		char * mem = new char[_block_size * _step];
		if (!mem) return false;

		for (size_t n = 0; n < _step; ++n) {
			Block * block = (Block *)(mem + n * _block_size);
			block->Init();
			block->next = _head->next;
			_head->next = block;
		}

		_mems.push_back(mem);
		return true;
	}

private:
	size_t _step;
	size_t _block_size;
	Block * _head;
	std::list<char *> _mems;
};

#endif//!	__POOL_H_INCLUDED__
